package com.zzl.java8.lambda6;

import com.gitee.yongzhuzl.commonutil.util.math.MathUtil;
import org.junit.jupiter.api.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.function.*;

/**
 * 参考链接：https://www.jianshu.com/p/86badf2c46c3
 * description: 理解并掌握Java8-Lambda表达式
 * @author 朱正磊
 * @date 2020-04-22 13:23:06
 */
public class UnderstandLambda {

    /**
     * Consumer<T> 测试消费型函数式接口
     */
    @Test
    void test1() {
        consume(1000D, e -> {
            System.out.println("今日全场8折");
            System.out.println("顾客消费：" + MathUtil.mulHalfUp(e.toString(), "0.8", 0) + "元");
        });

        consume(2000D, e -> {
            System.out.println("今日全场7折");
            System.out.println("顾客消费：" + MathUtil.mulHalfUp(e.toString(), "0.7", 0) + "元");
        });
    }

    /**
     * description: 商品打折
     * @author 朱正磊
     * @date 2020-04-22 14:31:32
     * @param money 商品原价
     * @param consumer 消费型函数式接口
     */
    private void consume(Double money, Consumer<Double> consumer) {
        consumer.accept(money);
    }

    /**
     * Supplier<T> 测试供给型函数式接口
     */
    @Test
    void test2() {
        // 生成10个100以内的随机数
        List<Integer> numList1 = getNumList(10, () -> (int) (Math.random() * 100));
        for (Integer num : numList1) {
            System.out.println(num);
        }

        // 生成5个10以内的随机数
        List<Integer> numList2 = getNumList(5, () -> (int) (Math.random() * 10));
        for (Integer num : numList2) {
            System.out.println(num);
        }
    }

    /**
     * description: 生成num个整数，并存入集合
     * @author 朱正磊
     * @date 2020-04-22 14:31:20
     * @param num 待生成的整数的个数
     * @param supplier 供给型函数式接口
     * @return 返回整数集合
     */
    private List<Integer> getNumList(Integer num, Supplier<Integer> supplier) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < num; i++) {
            Integer n = supplier.get();
            list.add(n);
        }
        return list;
    }

    /**
     * Function<T, R> 测试函数型函数式接口
     */
    @Test
    void test3() {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person(10, "zhangsan", "Male"));
        personList.add(new Person(20, "lisi", "Female"));
        personList.add(new Person(30, "wangwu", "Male"));
        personList.add(new Person(40, "zhaoliu", "Male"));
        personList.add(new Person(50, "tianqi", "Female"));
        personList.add(new Person(60, "zhuba", "Female"));
        personList.add(new Person(70, "sunjiu", "Male"));
        personList.add(new Person(80, "liushi", "Male"));
        // 类名调用实例方法，入参为传入实例对象（非静态方法、有1个参数为实例对象有返回值、类调用）
        List<String> nameList = map(personList, Person::getName);
        // [zhangsan, lisi, wangwu, zhaoliu, tianqi, zhuba, sunjiu, liushi]
        System.out.print(nameList);
    }

    /**
     * description: Function接口常用于数据的处理转换，比如给定一个Person对象列表，需要返回name列表
     * @author 朱正磊
     * @date 2020-04-22 15:11:14
     * @return 返回我们需要的集合列表
     */
    private <T, R> List<R> map(List<T> tList, Function<T, R> function) {
        List<R> rList = new ArrayList<>(tList.size());
        for (T t : tList) {
            rList.add(function.apply(t));
        }
        return rList;
    }

    /**
     * Predicate<T> 测试断言型函数式接口
     */
    @Test
    void test4() {
        List<Person> personList = new ArrayList<>();
        personList.add(new Person(10, "zhangsan", "Male"));
        personList.add(new Person(20, "lisi", "Female"));
        personList.add(new Person(30, "wangwu", "Male"));
        personList.add(new Person(40, "zhaoliu", "Male"));
        personList.add(new Person(50, "tianqi", "Female"));
        personList.add(new Person(60, "zhuba", "Female"));
        personList.add(new Person(70, "sunjiu", "Male"));
        personList.add(new Person(80, "liushi", "Male"));
        List<Person> newPersonList = filter(personList, person -> person.getAge() > 50);
        for (Person person : newPersonList) {
            System.out.println("姓名：" + person.getName() + "，年龄：" + person.getAge());
        }
    }

    /**
     * description: 采用断言型函数式接口进行业务过滤
     * @author 朱正磊
     * @date 2020-04-22 15:55:12
     * @param tList 待处理集合
     * @param predicate 断言型函数式接口
     * @param <T> 泛型
     * @return 返回处理之后的新集合
     */
    private <T> List<T> filter(List<T> tList, Predicate<T> predicate) {
        List<T> newtList = new ArrayList<>();
        for (T t : tList) {
            if (predicate.test(t)) {
                newtList.add(t);
            }
        }
        return newtList;
    }

    /**
     * 当要传递给Lambda体的操作，已经有实现的方法了，可以使用方法引用！
     * 方法引用：使用操作符::将方法名和对象或类的名字分隔开来。
     * 方法引用，主要有3种如下：
     *  对象::实例方法
     *  类::实例方法
     *  类::静态方法
     */
    @Test
    void test5() {
        // 1、对象::实例方法（采用Supplier）
        Person person = new Person();
        Supplier<String> supplier1 = () -> person.getName();
        Supplier<String> supplier2 = person::getName;

        // 2、类::实例方法（采用Function）
        Function<Person, String> function1 = p -> p.getName();
        Function<Person, String> function2 = Person::getName;

        // 3、类::静态方法
        // 计算x的y次方
        // 进化写法（简单写法）
        BinaryOperator<Double> binaryOperator1 = (x, y) -> Math.pow(x, y);
        BinaryOperator<Double> binaryOperator2 = Math::pow;

        // 原始写法（复杂写法）
        BiFunction<Double, Double, Double> biFunction1 = (x, y) -> Math.pow(x, y);
        BiFunction<Double, Double, Double> biFunction2 = Math::pow;
    }

    /**
     * 构造方法
     */
    @Test
    void test6() {
        // 构造方法只有一个参数
        Function<String, Person> function1 = name -> new Person(name);
        Function<String, Person> function2 = Person::new;

        // 构造方法有2个参数
        BiFunction<String, String, Person> function3 = (name, gender) -> new Person(name, gender);
        BiFunction<String, String, Person> function4 = Person::new;
    }

    /**
     * 接口中的默认方法和静态方法
     * Java8以前，接口里的方法要求全部是抽象方法，
     * Java8以后允许在接口里定义默认方法和静态方法，默认方法使用default关键字修饰。
     * 默认方法的主要优势是提供一种拓展接口的方法，而不破坏现有代码。
     */
    @Test
    void test7() {
        MyFunction myFunction = new MyFunctionImpl();
        // MyFunctionImpl实现抽象方法
        myFunction.func();
        // MyFunction默认方法（只实现MyFunction这一个接口的时候）
        myFunction.testDefault();
        // MyFunction静态方法
        MyFunction.testStatic();
    }

    /**
     * 接口冲突（default修饰的默认方法）
     */
    @Test
    void test8() {
        MyFunction myFunction = new MyFunctionImpl();
        // MyFunctionImpl实现抽象方法
        myFunction.func();
        // FunctionImpl覆盖接口中默认方法解决冲突（同时实现MyFunction和AnotherFunction这两个接口的时候）
        myFunction.testDefault();
        // MyFunction静态方法
        MyFunction.testStatic();
        // AnotherFunction静态方法
        AnotherFunction.testStatic();
    }

}
